<#-- moduleDisplayName and summary provided by data model -->

import { ChangeEvent, FormEvent, useEffect, useMemo, useState } from 'react';
import { api } from '../lib/api';
import type { PageResponse } from '../lib/api';
import type { ${classInfo.className}, ${classInfo.className}PrimaryKey } from '../types/${classInfo.className}';

const API_BASE = '/${apiBasePath}';
const PRIMARY_FIELD = '${options.primaryFieldName?default(classInfo.fieldList[0].fieldName)}';
const moduleDisplayName = '${moduleDisplayName?js_string}';
const moduleSummary = '${moduleSummary?js_string}';

<#function resolveFieldKind field>
  <#assign javaType = (field.fieldClass!"String")?lower_case>
  <#if javaType?contains("bigdecimal") || javaType?contains("decimal") || javaType?contains("double") || javaType?contains("float")>
    <#return "number">
  <#elseif javaType?contains("long") || javaType?contains("int") || javaType?contains("short") || javaType?contains("byte")>
    <#return "number">
  <#elseif javaType?contains("bool")>
    <#return "boolean">
  <#elseif javaType?contains("localdatetime") || javaType?contains("timestamp")>
    <#return "datetime">
  <#elseif javaType?contains("localdate") || javaType?contains("date")>
    <#return "date">
  <#else>
    <#return "string">
  </#if>
</#function>

<#function fieldLabel field>
  <#if field.fieldComment?has_content>
    <#return field.fieldComment?replace('\'', "\\'")?replace("\n", " ")>
  <#else>
    <#return field.fieldName>
  </#if>
</#function>

<#function fieldNameByColumn columnName>
  <#if !columnName?has_content>
    <#return "">
  </#if>
  <#assign normalized = columnName?lower_case>
  <#list classInfo.fieldList as field>
    <#if (field.columnName!"")?lower_case == normalized>
      <#return field.fieldName>
    </#if>
  </#list>
  <#return columnName>
</#function>

<#function fieldLabelByColumn columnName>
  <#if !columnName?has_content>
    <#return "">
  </#if>
  <#assign normalized = columnName?lower_case>
  <#list classInfo.fieldList as field>
    <#if (field.columnName!"")?lower_case == normalized>
      <#return fieldLabel(field)>
    </#if>
  </#list>
  <#return columnName>
</#function>

<#assign numberFields = []>
<#assign booleanFields = []>
<#assign dateFields = []>
<#assign primaryFieldName = options.primaryFieldName?default(classInfo.fieldList[0].fieldName)>
<#list classInfo.fieldList as field>
  <#assign kind = resolveFieldKind(field)>
  <#if kind == "number">
    <#assign numberFields = numberFields + [field.fieldName]>
  <#elseif kind == "boolean">
    <#assign booleanFields = booleanFields + [field.fieldName]>
  <#elseif kind == "datetime" || kind == "date">
    <#assign dateFields = dateFields + [field.fieldName]>
  </#if>
</#list>

const fieldConfigs = [
<#list classInfo.fieldList as field>
  {
    name: '${field.fieldName}',
    label: '${fieldLabel(field)}',
    isPrimary: ${(field.fieldName == primaryFieldName)?c},
    includeInForm: ${(field.fieldName != primaryFieldName)?c},
    type: '${resolveFieldKind(field)}'
  }<#if field_has_next>,</#if>
</#list>
] as const;

const numberFields = new Set<string>([
<#if numberFields?size gt 0>
  '${numberFields?join("', '")}'
</#if>
]);

const booleanFields = new Set<string>([
<#if booleanFields?size gt 0>
  '${booleanFields?join("', '")}'
</#if>
]);

const dateFields = new Set<string>([
<#if dateFields?size gt 0>
  '${dateFields?join("', '")}'
</#if>
]);

const COMPOSITE_UNIQUE_GROUPS = [
<#if compositeUniqueIndexes?has_content>
  <#list compositeUniqueIndexes as uniqueIndex>
    {
      key: ${((uniqueIndex.name?has_content)?then(uniqueIndex.name, 'composite_' + uniqueIndex_index))?js_string},
      fieldNames: [
        <#list uniqueIndex.columnNames![] as column>
          ${fieldNameByColumn(column)?js_string}<#if column_has_next>,</#if>
        </#list>
      ],
      fieldLabels: [
        <#list uniqueIndex.columnNames![] as column>
          ${fieldLabelByColumn(column)?js_string}<#if column_has_next>,</#if>
        </#list>
      ]
    }<#if uniqueIndex_has_next>,</#if>
  </#list>
</#if>
] as const;

const compositeUniqueFieldSet = new Set<string>(
  COMPOSITE_UNIQUE_GROUPS.reduce<string[]>((acc, group) => acc.concat(group.fieldNames), []),
);

const PAGE_SIZE_OPTIONS = [10, 20, 50];
const DEFAULT_PAGE_SIZE = PAGE_SIZE_OPTIONS[0];

type FormState = Record<string, any>;
type PageState = { page: number; size: number; total: number };

const buildEmptyForm = (): FormState => {
  const initial: FormState = {};
  fieldConfigs.forEach((field) => {
    if (field.includeInForm) {
      initial[field.name] = '';
    }
  });
  return initial;
};

const buildEmptyFilters = (): FormState => {
  const initial: FormState = {};
  fieldConfigs.forEach((field) => {
    initial[field.name] = '';
  });
  return initial;
};

const findIncompleteCompositeGroup = (source: FormState) => {
  for (const group of COMPOSITE_UNIQUE_GROUPS) {
    const values = group.fieldNames.map((name) => source[name]);
    const filledCount = values.filter((value) => value !== '' && value !== null && value !== undefined).length;
    if (filledCount > 0 && filledCount < group.fieldNames.length) {
      return group;
    }
  }
  return null;
};

const ${frontendPageComponentName} = () => {
  const [items, setItems] = useState<${classInfo.className}[]>([]);
  const [form, setForm] = useState<FormState>(() => buildEmptyForm());
  const [filters, setFilters] = useState<FormState>(() => buildEmptyFilters());
  const [pageState, setPageState] = useState<PageState>({ page: 1, size: DEFAULT_PAGE_SIZE, total: 0 });
  const [editingId, setEditingId] = useState<${classInfo.className}PrimaryKey | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const formFields = useMemo(() => fieldConfigs.filter((field) => field.includeInForm), []);
  const filterFields = useMemo(() => fieldConfigs, []);

  const buildQueryParams = (source: FormState, page: number, size: number) => {
    const params: Record<string, unknown> = { page, size };
    filterFields.forEach((field) => {
      const raw = source[field.name];
      if (raw === '' || raw === undefined) {
        return;
      }
      if (numberFields.has(field.name)) {
        const value = Number(raw);
        if (!Number.isNaN(value)) {
          params[field.name] = value;
        }
        return;
      }
      if (booleanFields.has(field.name)) {
        if (raw === '' || raw === undefined) {
          return;
        }
        if (raw === true || raw === 'true') {
          params[field.name] = true;
        } else if (raw === false || raw === 'false') {
          params[field.name] = false;
        }
        return;
      }
      params[field.name] = raw;
    });
    return params;
  };

  const loadData = async (
    targetPage = pageState.page,
    targetSize = pageState.size,
    overrideFilters: FormState = filters,
  ) => {
    setLoading(true);
    setError(null);
    try {
      const params = buildQueryParams(overrideFilters, targetPage, targetSize);
      const data = await api.get<PageResponse<${classInfo.className}> | ${classInfo.className}[]>(API_BASE, params);
      if (Array.isArray(data)) {
        setItems(data);
        setPageState({ page: targetPage, size: targetSize, total: data.length });
        return;
      }
      const records = Array.isArray(data.records) ? data.records : [];
      setItems(records);
      setPageState({
        page: Number(data.page ?? targetPage),
        size: Number(data.size ?? targetSize),
        total: Number(data.total ?? records.length),
      });
    } catch (err) {
      setError(err instanceof Error ? err.message : '加载数据失败');
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    void loadData(1, DEFAULT_PAGE_SIZE, filters);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const resetForm = () => {
    setForm(buildEmptyForm());
    setEditingId(null);
  };

  const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setError(null);
    const incompleteGroup = findIncompleteCompositeGroup(form);
    if (incompleteGroup) {
      setError(`请同时填写：${incompleteGroup.fieldLabels.join('、')}`);
      return;
    }
    setLoading(true);
    try {
      const payload = sanitizePayload(form);
      if (editingId !== null) {
        await api.put(`${'$'}{API_BASE}/${'$'}{editingId}`, payload);
      } else {
        await api.post(API_BASE, payload);
      }
      resetForm();
      await loadData(pageState.page, pageState.size);
    } catch (err) {
      setError(err instanceof Error ? err.message : '提交失败');
    } finally {
      setLoading(false);
    }
  };

  const handleEdit = (record: ${classInfo.className}) => {
    const next: FormState = {};
    formFields.forEach((field) => {
      const value = (record as Record<string, any>)[field.name];
      next[field.name] = toInputValue(field.name, value);
    });
    setForm(next);
    setEditingId((record as Record<string, any>)[PRIMARY_FIELD] as ${classInfo.className}PrimaryKey);
  };

  const handleDelete = async (id: ${classInfo.className}PrimaryKey) => {
    setLoading(true);
    setError(null);
    try {
      const nextPage = items.length <= 1 && pageState.page > 1 ? pageState.page - 1 : pageState.page;
      await api.remove(`${'$'}{API_BASE}/${'$'}{id}`);
      await loadData(nextPage, pageState.size);
    } catch (err) {
      setError(err instanceof Error ? err.message : '删除失败');
    } finally {
      setLoading(false);
    }
  };

  const handleFilterInputChange = (fieldName: string, value: string) => {
    setFilters((prev) => ({ ...prev, [fieldName]: value }));
  };

  const handleFilterSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const currentSize = pageState.size;
    setPageState((prev) => ({ ...prev, page: 1 }));
    void loadData(1, currentSize);
  };

  const handleFilterReset = () => {
    const nextFilters = buildEmptyFilters();
    const currentSize = pageState.size;
    setFilters(nextFilters);
    setPageState((prev) => ({ ...prev, page: 1 }));
    void loadData(1, currentSize, nextFilters);
  };

  const handlePageSizeChange = (event: ChangeEvent<HTMLSelectElement>) => {
    const nextSize = Number(event.target.value) || DEFAULT_PAGE_SIZE;
    setPageState((prev) => ({ ...prev, page: 1, size: nextSize }));
    void loadData(1, nextSize);
  };

  const handlePageChange = (nextPage: number) => {
    const totalPages = Math.max(1, Math.ceil((pageState.total || 0) / pageState.size));
    if (nextPage < 1 || nextPage > totalPages) {
      return;
    }
    void loadData(nextPage, pageState.size);
  };

  const sanitizePayload = (source: FormState) => {
    const payload: Record<string, any> = {};
    formFields.forEach((field) => {
      const raw = source[field.name];
      if (raw === '' || raw === undefined) {
        payload[field.name] = null;
        return;
      }
      if (numberFields.has(field.name)) {
        payload[field.name] = Number(raw);
        return;
      }
      if (booleanFields.has(field.name)) {
        payload[field.name] = raw === true || raw === 'true';
        return;
      }
      payload[field.name] = raw;
    });
    return payload;
  };

  const toInputValue = (fieldName: string, value: unknown) => {
    if (value === undefined || value === null) {
      return '';
    }
    if (booleanFields.has(fieldName)) {
      return value ? 'true' : 'false';
    }
    if (dateFields.has(fieldName) && typeof value === 'string') {
      return value.replace(' ', 'T');
    }
    return String(value);
  };

  const renderInput = (fieldName: string, label: string, type: string) => {
    const value = form[fieldName] ?? '';
    if (booleanFields.has(fieldName)) {
      return (
        <select
          id={fieldName}
          name={fieldName}
          value={value}
          onChange={(event) => setForm((prev) => ({ ...prev, [fieldName]: event.target.value }))}
        >
          <option value="">请选择</option>
          <option value="true">是</option>
          <option value="false">否</option>
        </select>
      );
    }

    const inputType = type === 'number' ? 'number' : type === 'datetime' ? 'datetime-local' : type === 'date' ? 'date' : 'text';
    return (
      <input
        id={fieldName}
        name={fieldName}
        value={value}
        onChange={(event) => setForm((prev) => ({ ...prev, [fieldName]: event.target.value }))}
        placeholder={`请输入${'$'}{label}`}
        type={inputType}
      />
    );
  };

  const renderFilterInput = (fieldName: string, label: string, type: string) => {
    const value = filters[fieldName] ?? '';
    if (booleanFields.has(fieldName)) {
      return (
        <select
          id={`filter-${'$'}{fieldName}`}
          name={`filter-${'$'}{fieldName}`}
          value={value}
          onChange={(event) => handleFilterInputChange(fieldName, event.target.value)}
        >
          <option value="">全部</option>
          <option value="true">是</option>
          <option value="false">否</option>
        </select>
      );
    }

    const inputType = type === 'number' ? 'number' : type === 'datetime' ? 'datetime-local' : type === 'date' ? 'date' : 'text';
    return (
      <input
        id={`filter-${'$'}{fieldName}`}
        name={`filter-${'$'}{fieldName}`}
        value={value}
        onChange={(event) => handleFilterInputChange(fieldName, event.target.value)}
        placeholder={`请输入${'$'}{label}`}
        type={inputType}
      />
    );
  };

  const renderCellValue = (fieldName: string, value: unknown) => {
    if (value === undefined || value === null) {
      return '-';
    }
    if (booleanFields.has(fieldName)) {
      return value ? '是' : '否';
    }
    if (dateFields.has(fieldName) && typeof value === 'string') {
      return value.replace('T', ' ');
    }
    return String(value);
  };

  const totalPages = Math.max(1, Math.ceil((pageState.total || 0) / pageState.size));

  return (
    <div className="page-stack">
      <section className="card">
        <div className="page-header">
          <div>
            <h2>{moduleDisplayName} 数据管理</h2>
            <p>{moduleSummary}</p>
          </div>
          <div className="page-actions">
            <button type="button" className="secondary" onClick={resetForm}>
              重置表单
            </button>
            <button
              type="button"
              className="pill-button ghost"
              onClick={() => void loadData(pageState.page, pageState.size)}
              disabled={loading}
            >
              刷新数据
            </button>
          </div>
        </div>
        {COMPOSITE_UNIQUE_GROUPS.length > 0 ? (
          <div className="info-banner" style={{ marginTop: 16 }}>
            <strong>提示：</strong>
            <span style={{ marginLeft: 8 }}>
              {COMPOSITE_UNIQUE_GROUPS.map((group, idx) => (
                <span key={group.key}>
                  {idx > 0 && '；'}
                  <strong>{group.fieldLabels.join(' + ')}</strong> 的组合不能重复
                </span>
              ))}
            </span>
          </div>
        ) : null}
        {error ? <div style={{ color: '#ef4444', marginTop: 12 }}>{error}</div> : null}
      </section>

      <section className="panel">
        <h3 style={{ marginTop: 0 }}>筛选条件</h3>
        <form onSubmit={handleFilterSubmit}>
          <div className="form-grid">
            {filterFields.map((field) => (
              <div key={`filter-${'$'}{field.name}`} className="form-field">
                <label htmlFor={`filter-${'$'}{field.name}`}>{field.label}</label>
                {renderFilterInput(field.name, field.label, field.type)}
              </div>
            ))}
          </div>
          <div className="page-actions" style={{ marginTop: 12 }}>
            <button type="submit" className="primary" disabled={loading}>
              查询
            </button>
            <button type="button" className="secondary" onClick={handleFilterReset} disabled={loading}>
              重置筛选
            </button>
          </div>
        </form>
      </section>

      <section className="panel">
        <h3 style={{ marginTop: 0 }}>{editingId === null ? '新增' + moduleDisplayName : '编辑' + moduleDisplayName}</h3>
        <form onSubmit={handleSubmit}>
          <div className="form-grid">
            {formFields.map((field) => (
              <div key={field.name} className="form-field">
                <label htmlFor={field.name}>
                  {field.label}
                </label>
                {renderInput(field.name, field.label, field.type)}
              </div>
            ))}
          </div>
          <div className="page-actions" style={{ marginTop: 12 }}>
            <button type="submit" className="primary" disabled={loading}>
              {editingId === null ? '保存并创建' : '保存更新'}
            </button>
            {editingId !== null ? (
              <button type="button" className="secondary" onClick={resetForm}>
                取消编辑
              </button>
            ) : null}
          </div>
        </form>
      </section>

      <section className="panel">
        <div className="page-header" style={{ marginBottom: 16 }}>
          <div>
            <h3 style={{ margin: 0 }}>数据列表</h3>
            <p style={{ margin: '6px 0 0', color: '#64748b', fontSize: 13 }}>支持分页、条件筛选、行级编辑与删除操作。</p>
          </div>
          <span style={{ fontSize: 13, color: '#94a3b8' }}>共 {pageState.total} 条记录</span>
        </div>
        <div style={{ overflowX: 'auto' }}>
          <table>
            <thead>
              <tr>
                {fieldConfigs.map((field) => (
                  <th key={field.name}>{field.label}</th>
                ))}
                <th style={{ width: 160 }}>操作</th>
              </tr>
            </thead>
            <tbody>
              {items.length === 0 ? (
                <tr>
                  <td colSpan={fieldConfigs.length + 1} style={{ textAlign: 'center', padding: 24 }}>
                    暂无数据
                  </td>
                </tr>
              ) : (
                items.map((item) => {
                  const primary = (item as Record<string, any>)[PRIMARY_FIELD] as ${classInfo.className}PrimaryKey;
                  return (
                    <tr key={String(primary)}>
                      {fieldConfigs.map((field) => (
                        <td key={field.name}>{renderCellValue(field.name, (item as Record<string, any>)[field.name])}</td>
                      ))}
                      <td>
                        <div className="table-actions">
                          <button type="button" className="secondary" onClick={() => handleEdit(item)}>
                            编辑
                          </button>
                          <button
                            type="button"
                            className="danger"
                            onClick={() => handleDelete(primary)}
                            disabled={loading}
                          >
                            删除
                          </button>
                        </div>
                      </td>
                    </tr>
                  );
                })
              )}
            </tbody>
          </table>
        </div>
        <div className="table-pagination" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 16, gap: 12, flexWrap: 'wrap' }}>
          <div style={{ color: '#64748b', fontSize: 13 }}>第 {pageState.page} / {totalPages} 页</div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
            <label htmlFor="page-size" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <span>每页</span>
              <select
                id="page-size"
                value={pageState.size}
                onChange={handlePageSizeChange}
                disabled={loading}
              >
                {PAGE_SIZE_OPTIONS.map((option) => (
                  <option key={option} value={option}>
                    {option}
                  </option>
                ))}
              </select>
            </label>
            <button
              type="button"
              className="secondary"
              onClick={() => handlePageChange(pageState.page - 1)}
              disabled={loading || pageState.page <= 1}
            >
              上一页
            </button>
            <button
              type="button"
              className="secondary"
              onClick={() => handlePageChange(pageState.page + 1)}
              disabled={loading || pageState.page >= totalPages}
            >
              下一页
            </button>
          </div>
        </div>
      </section>
    </div>
  );
};

export default ${frontendPageComponentName};

